1 Paper

  • Title: “Human-level control through deep reinforcement learning”
  • Authors: Volodymyr Mnih, Koray Kavukcuoglu, David Silver, Andrei A. Rusu, Joel Veness, Marc G. Bellemare, Alex Graves, Martin Riedmiller, Andreas K. Fidjeland, Georg Ostrovski, Stig Petersen, Charles Beattie, Amir Sadik, Ioannis Antonoglou, Helen King, Dharshan Kumaran, Daan Wierstra, Shane Legg & Demis Hassabis
  • Link: https://www.nature.com/articles/nature14236#citeas
  • Published: 25 February 2015

2 Preamble

Whether we are engaging in a daily conversation or driving a vehicle, essentially we are picking up on our surrounding environment and making a response simultaneously to the changes in the environment. That is, we are learning from our interaction with the environment and executing an action in return. This is the foundation idea that lies in all learning and intelligence (Sutton and Barto 2018). A machine learning technique to train software to make decision to achieve the optimal result by performing a sequence of actions in an environment is known as reinforcement learning (Sutton and Barto 2018). A reinforcement learning environment is formalized by an optimal control of Markov decision processes, which can be decompose into three essential parts - sensation, action, and goal (Sutton and Barto 2018). A learning agent should be able to sense the state of the environment, and takes series of actions that effects the state and achieve a goal overtime.

The idea of reinforcement learning is further extended to deep reinforcement learning that handles more sophisticated tasks. Deep reinforcement learning allows the agent to perform on real-world complexity in higher dimensions by combining reinforcement learning with a class of deep neural networks. A novel artificial agent, deep Q-network agent was recently introduced by Mnih et al. (2015). The authors implemented Q-learning with conventional neural networks, and evaluated their DQN agent with Atari 2600 and other game platforms. We will describe the mathematical concepts and learning process in the sections below to give a comprehensive introduction of deep reinforcement learning.

We will begin by introducing the fundamentals of reinforcement learning known as Q-learning, then dive into the deep learning deviation of the Q-learning algorithm, namely the deep Q-learning network (Mnih et al. 2015).

3 Summary of Notations

Variable Definition
\(s\) state
\(a\) action
\(t\) discrete time step
\(\pi\) policy, decision rule
\(s_t\) state at time \(t\)
\(a_t\) game actions selected from a set of actions
\(x_t\) result form emulator in the form of vector of pixel values
\(r_t\) reward in game
\(\gamma\) discount rate parameter per time step (default set to 0.99)
\(Q^*(s,a)\) maximum expected return achieved by policy
\(\theta\) weight parameter
\(R_t\) total reward at time t, dependent

4 Reinforcement Learning

Visualization of Reinforcement Learning

Figure 4.1: Visualization of Reinforcement Learning

Reinforcement learning is a way that allow computers to behave like human being. The computer will learn from the environment and make decision based on the learning. The diagram above shows the basic structure of reinforcement learning. The agent (the computer) interacts with the environment by taking actions and receiving rewards. The agent observes the state of the environment and selects an action based on the state. The environment then transitions to a new state and the agent receives a reward. The agent uses the reward to update its policy and selects a new action. This process continues until the agent reaches its goal. The goal of the agent is to maximize the cumulative reward over time.

Visualization of the work proposed by Mnih et al.

Figure 4.2: Visualization of the work proposed by Mnih et al.

Even though human being can interact with any environment based on this logic, it is hard to let computers have such performance. But instead of training an agent that can do every thing in the world, we can train different agents for different task. (Mnih et al. 2015) proposed an agent with some fascinating new features that can play old arcade games (Atari 2600 games). The agent is trained to play the games by observing the screen shots of the games (State \(s_t\)), the score of the game (Reward \(r_t\)) and by doing some magic the agent will take some action \(\alpha_t\) which it believes will let it win the game (Fig 4.2). After the agent input the action they make, the game will change to a new state \(s_{t+1}\) and the agent will receive a new reward \(r_{t+1}\) (sometimes don’t), and the agent will make a new action based on the new game stage until the game is over. We will discuss what is the magic that enable the agent to make decision and how will it let the agent learn how to play the game in the following chapters.

4.1 Q-learning Algorithm

A reinforcement learning environment follows a large finite Markov decision process (MDP). In reinforcement learning, MDP is a stochastic decision process that is comprised of 4 tuples: finite set of states, finite set of actions, state transition function, and reward function. The MDP also follows a Markov property, assuming the next state and the expected reward at \(t+1\) only depends on the state at time \(t\) and action at time \(t\) and not on any other prior events (Kaelbling, Littman, and Cassandra 1998). A policy is a behavior of an agent at a given time, and it takes the state as an input and returns action as output: \(\pi(s) \rightarrow a\). The goal of the agent is to select actions from a set of actions \(\{a_1,\cdots,a_k\}\) in a way that maximizes not only the current reward, but also the future reward. There’s a standard assumption in Q-learning that assumes the future rewards are discounted by a factor \(\gamma\) (default set to 0.99) per time step, so the future discounted return at time \(t\) is defined as:

\[ R_t = \sum^T_{t' = t} \gamma^{t-t'}r_{t'} \]

where T is the final time step, \(r_{t'}\) is the reward at time \(t'\), and the term \(\gamma^{t-t'}\) diminishes as we increase in time.

In order to teach the agent to achieve the maximum reward, and tie the reward to the actions, we can define the Optimal action-value function as the maximum expected return achieved by following some policy, after the agent seeing some sequence of states \(s\) and then taking some action \(a\):

\[ Q^*(s,a) = \max_{\pi} \mathbb{E}[R_t | s_t = s, a_t = a, \pi] \]

where, \(\pi\) is the policy to guide the action, \(s\) is the observed states, \(a\) is the current action, and \(R_t\) is the total reward at time \(t\).

In our case (learning how to play a game in Atari 2600), \(s\) can also be viewed as current states (screen shots of the game), because the consequence of previous actions and states are all reflected in the current state. The agent can make the best decision by observing the current state and taking the action that maximize the expected return.

This function obeys the Bellman equation, which is based on the following intuition: the expected return for taking the optimal action from a given state is the sum of the immediate reward from the current state to the next state, and the expected return from the next state to the goal state. Following this equation, The optimal action-value function (Q-function) can be expressed as:

\[ Q^*(s,a) = \mathbb{E}_{s'}[r + \gamma \max_{a'} Q^*(s',a') | s,a] \]

where \(r\) is the current reward, \(\gamma\) is the discount rate parameter, \(s'\) is the next state and \(a'\) is the next action.

Fig 4.3 shows a visualization of the learned action-value function in the game Pong. The top of the image shows the screenshots from Pong, and the bottom of the image depicts the potential action values that can be gained by taking these actions. Pong is a two-player game where the goal is to hit the ball past the opponent’s paddle. The DQN agent controls one of the paddles (green one) and the opponent is controlled by a simple AI. We can use the optimal action-value function to determine the optimal policy that our agent can follow. At time point 1, the ball is moving towards the paddle controlled by the agent on the right side of the screen and the q-values of all actions are around 0.7, reflecting the expected value of this state based on previous experience. At time point 2, the agent starts moving the paddle towards the ball and the value of the ‘up’ action stays high while the value of the ‘down’ action falls to −0.9. This reflects the fact that pressing ‘down’ would lead to the agent losing the ball and incurring a reward of −1 (losing the game). At time point 3, the agent hits the ball by pressing ‘up’, and the expected reward keeps increasing until time point 4, when the ball bounces to the left side of the screen and the value of all actions reflects that the agent is about to receive a reward of 1. Note, the dashed line shows the past trajectory of the ball purely for illustrative purposes (that is, not shown during the game).

Visualization of the learned action-value function in the game Pong

Figure 4.3: Visualization of the learned action-value function in the game Pong

4.2 Q-network: approximator of Q-value function

Achieving optimal target values: \(r + \gamma \max_{a'}Q^*(s',a')\) is hard and computationally expensive, especially when the state space is large. To address this issue, we can approximate the target values using a deep neural network as an approximator:

\[ r + \gamma \max_{a'}Q(s',a';\theta_i^-) \]

where \(\theta_i^-\) are the parameters from some previous iteration. The Q-network are defined as a neural network approximator with weights \(\theta\). The Q-network is trained to minimize the mean square error in the loss function:

\[ L_i(\theta_i) = \mathbb{E}_{s,a,r,s'}[(\underbrace{r + \gamma \max_{a'}Q(s',a';\theta_i^-)}_{\text{target value}} - \underbrace{Q(s,a;\theta_i)}_{\text{estimation}})^2] \]

and the gradient of the loss function can be written as:

\[ \nabla_{\theta_i}L_i(\theta_i) = \mathbb{E}_{s,a,r,s'}\left[\left(r + \gamma \max_{a'}Q(s',a';\theta_i^-) - Q(s,a;\theta_i)\right)\nabla_{\theta_i}Q(s,a;\theta_i)\right] \]

The target value: \(r + \gamma \max_{a'}Q(s',a';\theta_i^-)\) can be viewed as the reward you will get using parameters from some previous iteration, and the estimation: \(Q(s,a;\theta_i)\) is the reward you will get using the current parameters. The loss function is the squared difference between the target value and the estimation.

5 Deep Reinforcement Learning

The paper by Mnih et al. (2015) expands the Q-learning algorithm which described above by using a deep neural network to approximate the Q-value function with 3 new features covered below.

5.1 Convolutional network

Visualization of the network architecture

Figure 5.1: Visualization of the network architecture

The first new features used in the paper is the convolutional network used to process the image of the games into states which will be fed into the Q-network (shown in Fig 5.1).

The details of what is a convolutional network is beyond the scope of this project, but in short, a convolutional network is a type of neural network that is well-suited for processing images. More can be found in the following video made by 3Blue1Brown.

Before feeding the images to the network, some pre-processing need to be done as the raw images of the emulator (Atari 2600), which are 210 × 160 pixel images with a 128-colour palette, is demanding in terms of computation and memory requirements. First, to encode a single frame the authors take the maximum value for each pixel color value over the frame being encoded and the previous frame. This is necessary to remove flickering that is present in games where some objects appear only in even frames while other objects appear only in odd frames, an artefact caused by the limited number of sprites Atari 2600 can display at once. Second, authors then extract the Y channel, also known as luminance, from the RGB frame and rescale it to 84 × 84. The function (\(\phi\)) from algorithm 1 described below applies this pre-processing to the 4 most recent frames and stacks them to produce the input to the Q-function.

After image preprocessing, an 84 × 84 × 4 image will be used to train the network. The first hidden layer convolves 32 filters of 8 × 8 with stride 4 with the input image and applies a rectifier nonlinearity. The second hidden layer convolves 64 filters of 4 × 4 with stride 2, again followed by a rectifier nonlinearity. This is followed by a third convolutional layer that convolves 64 filters of 3 × 3 with stride 1 followed by a rectifier. The final hidden layer is fully-connected and consists of 512 rectifier units. The output layer is a fully-connected linear layer with a single output for each valid action (Q-value).

Two-dimensional t-SNE visualization of the representations in the last hidden layer assigned by the network to game states experienced while playing Space Invaders

Figure 5.2: Two-dimensional t-SNE visualization of the representations in the last hidden layer assigned by the network to game states experienced while playing Space Invaders

Fig 5.2 shows that the convolutional network is able to learn the representation of the game states that are useful for predicting the Q-values of the actions. The plots are generated by running t-SNE algorithem on the last hidden layer representations assigned by the network to game states experienced while playing Space Invaders. The dots are colored according to \(V\): the maximum expected reward of a state predicted by the agent (network) for the corresponding game states. The agent can separate full screens and nearly empty screens while predicting both of them with highest values because it learned that completing a screen leads to a new screen full of enemy ships. Partially completed screens are also separated from the full and empty screens, and the agent predicts them with lower values because less reward is available. The agent also learned to neglect the the orange bunkers when the game is near to the next level as they are not important for the reward.

5.2 Experience replay

The second new feature is the experience replay. The experience replay is a technique that randomly samples previous experiences from the agent’s memory and uses them to train the network. This technique is used to break the correlation between consecutive samples and stabilize the learning process. In order words, it prevent the agent from learning from only recent experiences, which are highly correlated with current state. The experience replay stores the agent’s experiences in a replay memory, which is a dataset of tuples \((s, a, r, s')\). Only the current state \(s\), action \(a\), reward \(r\), and next state \(s'\) are stored as any previous information is irrelevant for the agent to make the action. The agent samples a minibatch of experiences from the replay memory and uses them to train the network. The replay memory has a fixed size and when it is full, the oldest experiences are removed to make space for new experiences.

5.3 Second target network \(\widehat{Q}\)

The third new feature is the introduction of the target network \(\hat{Q}\). The agent will use \(\hat{Q}\) instead of \(Q\) to generates targets \(y_j = r + \gamma \max_{a'}Q(s',a';\theta_i^-)\) on each update. This will make divergence less likely to happen, as an update that increase \(Q(s_t, a_t)\) often also increase \(Q(s_{t+1},a) \forall a\). This implies that the target \(y_j\) might be increasing as well, leading to oscillations or divergence. By seting the target being calculated by another network which no not update for a certain period, the target will be fixed for a while, and making the learning process more stable. The target network in this study is set to be a copy of the Q-network, but only updated periodically.

Table 5.1: Comparison of the performance of the deep Q-network agent with and without experience replay and target Q-network on five Atari 2600 games. The value representing the highest average episode score. The results are taken from Mnih et al. (2015)
Game With replay, with target Q With replay, without target Q Without replay, with target Q Without replay, without target Q
Breakout 316.8 240.7 10.2 3.2
Enduro 1006.3 831.4 141.9 29.1
River Raid 7446.6 4102.8 2867.7 1453.0
Seaquest 2894.4 822.6 1003.0 275.8
Space Invaders 1088.9 826.3 373.2 302.0

Table 5.1 shows that the agent benefits from both experience replay and target Q-network, with the highest score achieved when both techniques are used.

5.4 Model Training

The authors use RMSProp algorithm (Root Mean Square Propagation, is an optimization algorithm is an extension of Stochastic Gradient Descent (SGD) algorithm in tranning deep neural networks) with a mini-batch size of 32 to train the agent (Bushaev 2018). The behavior policy during training is \(\epsilon\)-greedy policy with \(\epsilon\) annealed linearly from 1 to 0.1 over the first million frames and fixed at 0.1 thereafter. The agent is trained for 50 million frames, which is equivalent to 38 days of game time and use a replay memory of size one million. Both the reward and errors (\(r + \gamma \max_{a'}Q(s',a';\theta_i^-) - Q(s,a;\theta_i)\)) are clipped at [-1,1], and actions are only selected on every fourth frame and repeated for the next three frames. This is less expensive to run and has little effect on the performance as the fastest humane player can only react every 6th frame. Hyperparameters are selected by ‘informal search’ (not e.g. grid search).

5.5 Algorithm

Deep Q-learning with experience replay.

The above algorithm is describe in the paper by Mnih et al. (2015) and is used to train the deep Q-network agent. Episode is defined as an entire gameplay. The greedy parameter \(\epsilon\) allows the agent to explore the environment by selecting a random action with probability \(\epsilon\) and selecting the action with the highest Q-value with probability \(1-\epsilon\). \(\phi\) is the preprocessing function described above in 5.1. The target value \(y_j\) is calculated as \(r_j\) if the next state is terminal(end of the game), and \(r_j + \gamma \max_{a'} \hat{Q}(\phi_{j+1}, a'; \theta^-)\) if the next state is non-terminal. The target network \(\hat{Q}\) is updated every \(C = 10000\) steps to be the same as the Q-network \(Q\).

5.6 Results and evaluation

Traning curves tracking the agent's average score and average predicted action value

Figure 5.3: Traning curves tracking the agent’s average score and average predicted action value

Fig 5.3 shows the agent’s average score and average predicted action value over the course of training. The agent’s average score increases over time as the agent learns to play the game better. The average predicted action value also increases over time, indicating that the agent is learning to predict the value of actions more accurately. In easier games, the agent learns to play the game well quickly, and the scores are more stable, while in harder games, the agent takes longer to learn to play the game well and the results are more variable.

Comparison of the DQN agent with the best reinforcement learning methods in the literature

Figure 5.4: Comparison of the DQN agent with the best reinforcement learning methods in the literature

The authors further evaluate the agent in all 49 games in the Atari 2600 platform and compare the results with the best reinforcement learning methods in the literature. Fig 5.4 summarizes the results of the comparison. The deep Q-network agent outperforms the best reinforcement learning methods and professional human game testers in majority of the games. It is worth to notice that the best performance games are those that are easier to play and easier for the agent to learn (e.g: Boxing, Breakout, Star gunner, Pinball, etc.). These games are short-horizon games, and the ultimate game strategy is to gain more points in the game. However, for long-horizon games that doesn’t give immediate reward and with a longer time to achieve a goal, the agent may not perform well in these cases. Montezuma’s Revenge is one of the hardest games in the Atari 2600 platform, which require the player to control the character to explore the maze and collect the keys to open the doors (showed in Fig 5.5 (Brothers 2021)). The reward will only be given when the player reach the end of the maze and open the door. The evaluation shows that the agent is basically play the game randomly (0% performance) and did not learn the strategy to play the game.

Screenshot of the game Montezuma's Revenge

Figure 5.5: Screenshot of the game Montezuma’s Revenge

This video (Deepmind 2016) demonstrates how an agent is showing improvement over training episodes and is able to pick up the optimal strategy at hitting the bricks to gain higher score in the game.

5.7 Summary

  • Deep reinforcement learning is adapted from reinforcement learning, with abilities to learn more complex policies from high dimensional sensory input
  • The goal is to maximize the cumulative future reward
  • DQN implements convolutional neural network as an approximator for the target values
  • Perform experience replay to remove correlations and stores the agent’s experiences in a replay memory
  • Minimize the mean square error between Q-network and Q-learning target
  • The algorithm uses RMSProp with stochastic gradient descent to update the weights
  • The DQN was tested on 49 Atari 2600 games, and it outperforms other reinforcement learning algorithms

6 References

Brothers, Parker. 2021. “Montezuma’s Revenge (Video Game) Atari 8-Bit PAL Screenshot.”
Bushaev, Vitaly. 2018. “Understanding RMSprop — Faster Neural Network Learning.” 2018. https://towardsdatascience.com/understanding-rmsprop-faster-neural-network-learning-62e116fcf29a.
Deepmind, Google. 2016. “DQN Breakout.” https://youtu.be/TmPfTpjtdgg: Youtube.
Kaelbling, Leslie Pack, Michael L. Littman, and Anthony R. Cassandra. 1998. “Planning and Acting in Partially Observable Stochastic Domains.” Artificial Intelligence 101 (1): 99–134. https://doi.org/https://doi.org/10.1016/S0004-3702(98)00023-X.
Mnih, Volodymyr, Koray Kavukcuoglu, David Silver, Andrei A. Rusu, Joel Veness, Marc G. Bellemare, Alex Graves, et al. 2015. “Human-Level Control Through Deep Reinforcement Learning.” Nature 518 (7540): 529–33. https://doi.org/10.1038/nature14236.
Sutton, R. S., and A. G. Barto. 2018. Reinforcement Learning, Second Edition: An Introduction. Adaptive Computation and Machine Learning Series. MIT Press. https://books.google.com/books?id=5s-MEAAAQBAJ.
LS0tCnRpdGxlOiAiQSBDb21wcmVoZW5zaXZlIEludHJvZHVjdGlvbiB0byBEZWVwIFJlaW5mb3JjZW1lbnQgTGVhcm5pbmciCmF1dGhvcjogIk1pYW95YW4gQ2hlbiBhbmQgWmhhb3hpYW5nIERpbmciCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKaGVhZGVyLWluY2x1ZGVzOgogICAtIFx1c2VwYWNrYWdle2FsZ29yaXRobX0KICAgLSBcdXNlcGFja2FnZXthbGdwc2V1ZG9jb2RlfQpvdXRwdXQ6CiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0aGVtZTogdW5pdGVkCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBrZWVwX21kOiB0cnVlCmJpYmxpb2dyYXBoeTogZGVlcFJMLnJlZmVyZW5jZXMuYmliCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gRkFMU0UpCmtuaXRyOjpvcHRzX2NodW5rJHNldChtZXNzYWdlID0gRikKa25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSBGKQprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLmhlaWdodCA9IDQpCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGggPSA2KQprbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbCA9IFQpCmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcuYWxpZ249ImNlbnRlciIpCmBgYAoKIyBQYXBlcgotICAgKipUaXRsZSoqOiAiSHVtYW4tbGV2ZWwgY29udHJvbCB0aHJvdWdoIGRlZXAgcmVpbmZvcmNlbWVudCBsZWFybmluZyIKLSAgICoqQXV0aG9ycyoqOiBWb2xvZHlteXIgTW5paCwgS29yYXkgS2F2dWtjdW9nbHUsIERhdmlkIFNpbHZlciwgQW5kcmVpIEEuIFJ1c3UsIEpvZWwgVmVuZXNzLCBNYXJjIEcuIEJlbGxlbWFyZSwgQWxleCBHcmF2ZXMsIE1hcnRpbiBSaWVkbWlsbGVyLCBBbmRyZWFzIEsuIEZpZGplbGFuZCwgR2VvcmcgT3N0cm92c2tpLCBTdGlnIFBldGVyc2VuLCBDaGFybGVzIEJlYXR0aWUsIEFtaXIgU2FkaWssIElvYW5uaXMgQW50b25vZ2xvdSwgSGVsZW4gS2luZywgRGhhcnNoYW4gS3VtYXJhbiwgRGFhbiBXaWVyc3RyYSwgU2hhbmUgTGVnZyAmIERlbWlzIEhhc3NhYmlzIAotICAgKipMaW5rKio6IGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvbmF0dXJlMTQyMzYjY2l0ZWFzCi0gICAqKlB1Ymxpc2hlZCoqOiAyNSBGZWJydWFyeSAyMDE1CgojIFByZWFtYmxlCgpXaGV0aGVyIHdlIGFyZSBlbmdhZ2luZyBpbiBhIGRhaWx5IGNvbnZlcnNhdGlvbiBvciBkcml2aW5nIGEgdmVoaWNsZSwgZXNzZW50aWFsbHkgd2UgYXJlIHBpY2tpbmcgdXAgb24gb3VyIHN1cnJvdW5kaW5nIGVudmlyb25tZW50IGFuZCBtYWtpbmcgYSByZXNwb25zZSBzaW11bHRhbmVvdXNseSB0byB0aGUgY2hhbmdlcyBpbiB0aGUgZW52aXJvbm1lbnQuIFRoYXQgaXMsIHdlIGFyZSBsZWFybmluZyBmcm9tIG91ciBpbnRlcmFjdGlvbiB3aXRoIHRoZSBlbnZpcm9ubWVudCBhbmQgZXhlY3V0aW5nIGFuIGFjdGlvbiBpbiByZXR1cm4uIFRoaXMgaXMgdGhlIGZvdW5kYXRpb24gaWRlYSB0aGF0IGxpZXMgaW4gYWxsIGxlYXJuaW5nIGFuZCBpbnRlbGxpZ2VuY2UgW0BzdXR0b24yMDE4XS4gQSBtYWNoaW5lIGxlYXJuaW5nIHRlY2huaXF1ZSB0byB0cmFpbiBzb2Z0d2FyZSB0byBtYWtlIGRlY2lzaW9uIHRvIGFjaGlldmUgdGhlIG9wdGltYWwgcmVzdWx0IGJ5IHBlcmZvcm1pbmcgYSBzZXF1ZW5jZSBvZiBhY3Rpb25zIGluIGFuIGVudmlyb25tZW50IGlzIGtub3duIGFzICpyZWluZm9yY2VtZW50IGxlYXJuaW5nKiBbQHN1dHRvbjIwMThdLiBBIHJlaW5mb3JjZW1lbnQgbGVhcm5pbmcgZW52aXJvbm1lbnQgaXMgZm9ybWFsaXplZCBieSBhbiBvcHRpbWFsIGNvbnRyb2wgb2YgTWFya292IGRlY2lzaW9uIHByb2Nlc3Nlcywgd2hpY2ggY2FuIGJlIGRlY29tcG9zZSBpbnRvIHRocmVlIGVzc2VudGlhbCBwYXJ0cyAtIHNlbnNhdGlvbiwgYWN0aW9uLCBhbmQgZ29hbCBbQHN1dHRvbjIwMThdLiBBIGxlYXJuaW5nIGFnZW50IHNob3VsZCBiZSBhYmxlIHRvIHNlbnNlIHRoZSBzdGF0ZSBvZiB0aGUgZW52aXJvbm1lbnQsIGFuZCB0YWtlcyBzZXJpZXMgb2YgYWN0aW9ucyB0aGF0IGVmZmVjdHMgdGhlIHN0YXRlIGFuZCBhY2hpZXZlIGEgZ29hbCBvdmVydGltZS4KClRoZSBpZGVhIG9mIHJlaW5mb3JjZW1lbnQgbGVhcm5pbmcgaXMgZnVydGhlciBleHRlbmRlZCB0byAqZGVlcCByZWluZm9yY2VtZW50IGxlYXJuaW5nKiB0aGF0IGhhbmRsZXMgbW9yZSBzb3BoaXN0aWNhdGVkIHRhc2tzLiBEZWVwIHJlaW5mb3JjZW1lbnQgbGVhcm5pbmcgYWxsb3dzIHRoZSBhZ2VudCB0byBwZXJmb3JtIG9uIHJlYWwtd29ybGQgY29tcGxleGl0eSBpbiBoaWdoZXIgZGltZW5zaW9ucyBieSBjb21iaW5pbmcgcmVpbmZvcmNlbWVudCBsZWFybmluZyB3aXRoIGEgY2xhc3Mgb2YgZGVlcCBuZXVyYWwgbmV0d29ya3MuIEEgbm92ZWwgYXJ0aWZpY2lhbCBhZ2VudCwgZGVlcCBRLW5ldHdvcmsgYWdlbnQgd2FzIHJlY2VudGx5IGludHJvZHVjZWQgYnkgTW5paCBldCBhbC4gKDIwMTUpLiBUaGUgYXV0aG9ycyBpbXBsZW1lbnRlZCBRLWxlYXJuaW5nIHdpdGggY29udmVudGlvbmFsIG5ldXJhbCBuZXR3b3JrcywgYW5kIGV2YWx1YXRlZCB0aGVpciBEUU4gYWdlbnQgd2l0aCBBdGFyaSAyNjAwIGFuZCBvdGhlciBnYW1lIHBsYXRmb3Jtcy4gV2Ugd2lsbCBkZXNjcmliZSB0aGUgbWF0aGVtYXRpY2FsIGNvbmNlcHRzIGFuZCBsZWFybmluZyBwcm9jZXNzIGluIHRoZSBzZWN0aW9ucyBiZWxvdyB0byBnaXZlIGEgY29tcHJlaGVuc2l2ZSBpbnRyb2R1Y3Rpb24gb2YgZGVlcCByZWluZm9yY2VtZW50IGxlYXJuaW5nLgoKV2Ugd2lsbCBiZWdpbiBieSBpbnRyb2R1Y2luZyB0aGUgZnVuZGFtZW50YWxzIG9mIHJlaW5mb3JjZW1lbnQgbGVhcm5pbmcga25vd24gYXMgUS1sZWFybmluZywgdGhlbiBkaXZlIGludG8gdGhlIGRlZXAgbGVhcm5pbmcgZGV2aWF0aW9uIG9mIHRoZSBRLWxlYXJuaW5nIGFsZ29yaXRobSwgbmFtZWx5IHRoZSBkZWVwIFEtbGVhcm5pbmcgbmV0d29yayBbQE1uaWgyMDE1XS4KCiMgU3VtbWFyeSBvZiBOb3RhdGlvbnMKCnwgVmFyaWFibGUgICB8IERlZmluaXRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfC0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfAp8ICRzJCAgICAgICAgfCBzdGF0ZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgJGEkICAgICAgICB8IGFjdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAkdCQgICAgICAgIHwgZGlzY3JldGUgdGltZSBzdGVwICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8ICRccGkkICAgICAgfCBwb2xpY3ksIGRlY2lzaW9uIHJ1bGUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAkc190JCAgICAgIHwgc3RhdGUgYXQgdGltZSAkdCQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8ICRhX3QkICAgICAgfCBnYW1lIGFjdGlvbnMgc2VsZWN0ZWQgZnJvbSBhIHNldCBvZiBhY3Rpb25zICAgICAgICAgICAgICAgICB8CnwgJHhfdCQgICAgICB8IHJlc3VsdCBmb3JtIGVtdWxhdG9yIGluIHRoZSBmb3JtIG9mIHZlY3RvciBvZiBwaXhlbCB2YWx1ZXMgIHwKfCAkcl90JCAgICAgIHwgcmV3YXJkIGluIGdhbWUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8ICRcZ2FtbWEkICAgfCBkaXNjb3VudCByYXRlIHBhcmFtZXRlciBwZXIgdGltZSBzdGVwIChkZWZhdWx0IHNldCB0byAwLjk5KSB8CnwgJFFeKihzLGEpJCB8IG1heGltdW0gZXhwZWN0ZWQgcmV0dXJuIGFjaGlldmVkIGJ5IHBvbGljeSAgICAgICAgICAgICAgICAgIHwKfCAkXHRoZXRhJCAgIHwgd2VpZ2h0IHBhcmFtZXRlciAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8ICRSX3QkICAgICAgfCB0b3RhbCByZXdhcmQgYXQgdGltZSB0LCBkZXBlbmRlbnQgICAgICAgICAgICAgICAgICAgICAgICAgICB8CgojIFJlaW5mb3JjZW1lbnQgTGVhcm5pbmcKYGBge3IgZmlnLmNhcD0iVmlzdWFsaXphdGlvbiBvZiBSZWluZm9yY2VtZW50IExlYXJuaW5nIiwgZmlnLmhlaWdodD0zLCBmaWcud2lkdGg9NX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImRpYWdyYW0ucG5nIikKYGBgCgpSZWluZm9yY2VtZW50IGxlYXJuaW5nIGlzIGEgd2F5IHRoYXQgYWxsb3cgY29tcHV0ZXJzIHRvIGJlaGF2ZSBsaWtlIGh1bWFuIGJlaW5nLiBUaGUgY29tcHV0ZXIgd2lsbCBsZWFybiBmcm9tIHRoZSBlbnZpcm9ubWVudCBhbmQgbWFrZSBkZWNpc2lvbiBiYXNlZCBvbiB0aGUgbGVhcm5pbmcuIFRoZSBkaWFncmFtIGFib3ZlIHNob3dzIHRoZSBiYXNpYyBzdHJ1Y3R1cmUgb2YgcmVpbmZvcmNlbWVudCBsZWFybmluZy4gVGhlIGFnZW50ICh0aGUgY29tcHV0ZXIpIGludGVyYWN0cyB3aXRoIHRoZSBlbnZpcm9ubWVudCBieSB0YWtpbmcgYWN0aW9ucyBhbmQgcmVjZWl2aW5nIHJld2FyZHMuIFRoZSBhZ2VudCBvYnNlcnZlcyB0aGUgc3RhdGUgb2YgdGhlIGVudmlyb25tZW50IGFuZCBzZWxlY3RzIGFuIGFjdGlvbiBiYXNlZCBvbiB0aGUgc3RhdGUuIFRoZSBlbnZpcm9ubWVudCB0aGVuIHRyYW5zaXRpb25zIHRvIGEgbmV3IHN0YXRlIGFuZCB0aGUgYWdlbnQgcmVjZWl2ZXMgYSByZXdhcmQuIFRoZSBhZ2VudCB1c2VzIHRoZSByZXdhcmQgdG8gdXBkYXRlIGl0cyBwb2xpY3kgYW5kIHNlbGVjdHMgYSBuZXcgYWN0aW9uLiBUaGlzIHByb2Nlc3MgY29udGludWVzIHVudGlsIHRoZSBhZ2VudCByZWFjaGVzIGl0cyBnb2FsLiBUaGUgZ29hbCBvZiB0aGUgYWdlbnQgaXMgdG8gbWF4aW1pemUgdGhlIGN1bXVsYXRpdmUgcmV3YXJkIG92ZXIgdGltZS4KCmBgYHtyIG5ldHdvcmstaWxsdXN0cmF0aW9uLCBmaWcuY2FwPSJWaXN1YWxpemF0aW9uIG9mIHRoZSB3b3JrIHByb3Bvc2VkIGJ5IE1uaWggZXQgYWwuIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImZpZy1pbGx1c3RyYXRpb24ucG5nIikKYGBgCgpFdmVuIHRob3VnaCBodW1hbiBiZWluZyBjYW4gaW50ZXJhY3Qgd2l0aCBhbnkgZW52aXJvbm1lbnQgYmFzZWQgb24gdGhpcyBsb2dpYywgaXQgaXMgaGFyZCB0byBsZXQgY29tcHV0ZXJzIGhhdmUgc3VjaCBwZXJmb3JtYW5jZS4gQnV0IGluc3RlYWQgb2YgdHJhaW5pbmcgYW4gYWdlbnQgdGhhdCBjYW4gZG8gZXZlcnkgdGhpbmcgaW4gdGhlIHdvcmxkLCB3ZSBjYW4gdHJhaW4gZGlmZmVyZW50IGFnZW50cyBmb3IgZGlmZmVyZW50IHRhc2suIFtATW5paDIwMTVdIHByb3Bvc2VkIGFuIGFnZW50IHdpdGggc29tZSBmYXNjaW5hdGluZyBuZXcgZmVhdHVyZXMgdGhhdCBjYW4gcGxheSBvbGQgYXJjYWRlIGdhbWVzIChBdGFyaSAyNjAwIGdhbWVzKS4gVGhlIGFnZW50IGlzIHRyYWluZWQgdG8gcGxheSB0aGUgZ2FtZXMgYnkgb2JzZXJ2aW5nIHRoZSBzY3JlZW4gc2hvdHMgb2YgdGhlIGdhbWVzIChTdGF0ZSAkc190JCksIHRoZSBzY29yZSBvZiB0aGUgZ2FtZSAoUmV3YXJkICRyX3QkKSBhbmQgYnkgZG9pbmcgc29tZSBtYWdpYyB0aGUgYWdlbnQgd2lsbCB0YWtlIHNvbWUgYWN0aW9uICRcYWxwaGFfdCQgd2hpY2ggaXQgYmVsaWV2ZXMgd2lsbCBsZXQgaXQgd2luIHRoZSBnYW1lIChGaWcgXEByZWYoZmlnOm5ldHdvcmstaWxsdXN0cmF0aW9uKSkuIEFmdGVyIHRoZSBhZ2VudCBpbnB1dCB0aGUgYWN0aW9uIHRoZXkgbWFrZSwgdGhlIGdhbWUgd2lsbCBjaGFuZ2UgdG8gYSBuZXcgc3RhdGUgJHNfe3QrMX0kIGFuZCB0aGUgYWdlbnQgd2lsbCByZWNlaXZlIGEgbmV3IHJld2FyZCAkcl97dCsxfSQgKHNvbWV0aW1lcyBkb24ndCksIGFuZCB0aGUgYWdlbnQgd2lsbCBtYWtlIGEgbmV3IGFjdGlvbiBiYXNlZCBvbiB0aGUgbmV3IGdhbWUgc3RhZ2UgdW50aWwgdGhlIGdhbWUgaXMgb3Zlci4gV2Ugd2lsbCBkaXNjdXNzIHdoYXQgaXMgdGhlIG1hZ2ljIHRoYXQgZW5hYmxlIHRoZSBhZ2VudCB0byBtYWtlIGRlY2lzaW9uIGFuZCBob3cgd2lsbCBpdCBsZXQgdGhlIGFnZW50IGxlYXJuIGhvdyB0byBwbGF5IHRoZSBnYW1lIGluIHRoZSBmb2xsb3dpbmcgY2hhcHRlcnMuCgojIyBRLWxlYXJuaW5nIEFsZ29yaXRobQoKQSByZWluZm9yY2VtZW50IGxlYXJuaW5nIGVudmlyb25tZW50IGZvbGxvd3MgYSBsYXJnZSBmaW5pdGUgKipNYXJrb3YgZGVjaXNpb24gcHJvY2VzcyoqIChNRFApLiBJbiByZWluZm9yY2VtZW50IGxlYXJuaW5nLCBNRFAgaXMgYSBzdG9jaGFzdGljIGRlY2lzaW9uIHByb2Nlc3MgdGhhdCBpcyBjb21wcmlzZWQgb2YgNCB0dXBsZXM6IGZpbml0ZSBzZXQgb2Ygc3RhdGVzLCBmaW5pdGUgc2V0IG9mIGFjdGlvbnMsIHN0YXRlIHRyYW5zaXRpb24gZnVuY3Rpb24sIGFuZCByZXdhcmQgZnVuY3Rpb24uIFRoZSBNRFAgYWxzbyBmb2xsb3dzIGEgTWFya292IHByb3BlcnR5LCBhc3N1bWluZyB0aGUgbmV4dCBzdGF0ZSBhbmQgdGhlIGV4cGVjdGVkIHJld2FyZCBhdCAkdCsxJCBvbmx5IGRlcGVuZHMgb24gdGhlIHN0YXRlIGF0IHRpbWUgJHQkIGFuZCBhY3Rpb24gYXQgdGltZSAkdCQgYW5kIG5vdCBvbiBhbnkgb3RoZXIgcHJpb3IgZXZlbnRzIFtAS2FlbGJsaW5nMTk5OF0uIEEgKipwb2xpY3kqKiBpcyBhIGJlaGF2aW9yIG9mIGFuIGFnZW50IGF0IGEgZ2l2ZW4gdGltZSwgYW5kIGl0IHRha2VzIHRoZSBzdGF0ZSBhcyBhbiBpbnB1dCBhbmQgcmV0dXJucyBhY3Rpb24gYXMgb3V0cHV0OiAkXHBpKHMpIFxyaWdodGFycm93IGEkLiBUaGUgZ29hbCBvZiB0aGUgYWdlbnQgaXMgdG8gc2VsZWN0IGFjdGlvbnMgZnJvbSBhIHNldCBvZiBhY3Rpb25zICRce2FfMSxcY2RvdHMsYV9rXH0kIGluIGEgd2F5IHRoYXQgbWF4aW1pemVzIG5vdCBvbmx5IHRoZSBjdXJyZW50IHJld2FyZCwgYnV0IGFsc28gdGhlIGZ1dHVyZSByZXdhcmQuIFRoZXJlJ3MgYSBzdGFuZGFyZCBhc3N1bXB0aW9uIGluIFEtbGVhcm5pbmcgdGhhdCBhc3N1bWVzIHRoZSBmdXR1cmUgcmV3YXJkcyBhcmUgZGlzY291bnRlZCBieSBhIGZhY3RvciAkXGdhbW1hJCAoZGVmYXVsdCBzZXQgdG8gMC45OSkgcGVyIHRpbWUgc3RlcCwgc28gdGhlIGZ1dHVyZSBkaXNjb3VudGVkIHJldHVybiBhdCB0aW1lICR0JCBpcyBkZWZpbmVkIGFzOgoKJCQKUl90ID0gXHN1bV5UX3t0JyA9IHR9IFxnYW1tYV57dC10J31yX3t0J30KJCQKCndoZXJlIFQgaXMgdGhlIGZpbmFsIHRpbWUgc3RlcCwgJHJfe3QnfSQgaXMgdGhlIHJld2FyZCBhdCB0aW1lICR0JyQsIGFuZCB0aGUgdGVybSAkXGdhbW1hXnt0LXQnfSQgZGltaW5pc2hlcyBhcyB3ZSBpbmNyZWFzZSBpbiB0aW1lLgoKSW4gb3JkZXIgdG8gdGVhY2ggdGhlIGFnZW50IHRvIGFjaGlldmUgdGhlIG1heGltdW0gcmV3YXJkLCBhbmQgdGllIHRoZSByZXdhcmQgdG8gdGhlIGFjdGlvbnMsIHdlIGNhbiBkZWZpbmUgdGhlICoqT3B0aW1hbCBhY3Rpb24tdmFsdWUgZnVuY3Rpb24qKiBhcyB0aGUgbWF4aW11bSBleHBlY3RlZCByZXR1cm4gYWNoaWV2ZWQgYnkgZm9sbG93aW5nIHNvbWUgcG9saWN5LCBhZnRlciB0aGUgYWdlbnQgc2VlaW5nIHNvbWUgc2VxdWVuY2Ugb2Ygc3RhdGVzICRzJCBhbmQgdGhlbiB0YWtpbmcgc29tZSBhY3Rpb24gJGEkOgoKJCQKUV4qKHMsYSkgPSBcbWF4X3tccGl9IFxtYXRoYmJ7RX1bUl90IHwgc190ID0gcywgYV90ID0gYSwgXHBpXQokJAoKd2hlcmUsICRccGkkIGlzIHRoZSBwb2xpY3kgdG8gZ3VpZGUgdGhlIGFjdGlvbiwgJHMkIGlzIHRoZSBvYnNlcnZlZCBzdGF0ZXMsICRhJCBpcyB0aGUgY3VycmVudCBhY3Rpb24sIGFuZCAkUl90JCBpcyB0aGUgdG90YWwgcmV3YXJkIGF0IHRpbWUgJHQkLgoKSW4gb3VyIGNhc2UgKGxlYXJuaW5nIGhvdyB0byBwbGF5IGEgZ2FtZSBpbiBBdGFyaSAyNjAwKSwgJHMkIGNhbiBhbHNvIGJlIHZpZXdlZCBhcyBjdXJyZW50IHN0YXRlcyAoc2NyZWVuIHNob3RzIG9mIHRoZSBnYW1lKSwgYmVjYXVzZSB0aGUgY29uc2VxdWVuY2Ugb2YgcHJldmlvdXMgYWN0aW9ucyBhbmQgc3RhdGVzIGFyZSBhbGwgcmVmbGVjdGVkIGluIHRoZSBjdXJyZW50IHN0YXRlLiBUaGUgYWdlbnQgY2FuIG1ha2UgdGhlIGJlc3QgZGVjaXNpb24gYnkgb2JzZXJ2aW5nIHRoZSBjdXJyZW50IHN0YXRlIGFuZCB0YWtpbmcgdGhlIGFjdGlvbiB0aGF0IG1heGltaXplIHRoZSBleHBlY3RlZCByZXR1cm4uCgpUaGlzIGZ1bmN0aW9uIG9iZXlzIHRoZSAqQmVsbG1hbiBlcXVhdGlvbiosIHdoaWNoIGlzIGJhc2VkIG9uIHRoZSBmb2xsb3dpbmcgaW50dWl0aW9uOiB0aGUgZXhwZWN0ZWQgcmV0dXJuIGZvciB0YWtpbmcgdGhlIG9wdGltYWwgYWN0aW9uIGZyb20gYSBnaXZlbiBzdGF0ZSBpcyB0aGUgc3VtIG9mIHRoZSBpbW1lZGlhdGUgcmV3YXJkIGZyb20gdGhlIGN1cnJlbnQgc3RhdGUgdG8gdGhlIG5leHQgc3RhdGUsIGFuZCB0aGUgZXhwZWN0ZWQgcmV0dXJuIGZyb20gdGhlIG5leHQgc3RhdGUgdG8gdGhlIGdvYWwgc3RhdGUuIEZvbGxvd2luZyB0aGlzIGVxdWF0aW9uLCBUaGUgb3B0aW1hbCBhY3Rpb24tdmFsdWUgZnVuY3Rpb24gKFEtZnVuY3Rpb24pIGNhbiBiZSBleHByZXNzZWQgYXM6CgokJApRXioocyxhKSA9IFxtYXRoYmJ7RX1fe3MnfVtyICsgXGdhbW1hIFxtYXhfe2EnfSBRXioocycsYScpIHwgcyxhXQokJCAKCndoZXJlICRyJCBpcyB0aGUgY3VycmVudCByZXdhcmQsICRcZ2FtbWEkIGlzIHRoZSBkaXNjb3VudCByYXRlIHBhcmFtZXRlciwgJHMnJCBpcyB0aGUgbmV4dCBzdGF0ZSBhbmQgJGEnJCBpcyB0aGUgbmV4dCBhY3Rpb24uCgpGaWcgXEByZWYoZmlnOnF2YWx1ZSkgc2hvd3MgYSB2aXN1YWxpemF0aW9uIG9mIHRoZSBsZWFybmVkIGFjdGlvbi12YWx1ZSBmdW5jdGlvbiBpbiB0aGUgZ2FtZSBQb25nLiBUaGUgdG9wIG9mIHRoZSBpbWFnZSBzaG93cyB0aGUgc2NyZWVuc2hvdHMgZnJvbSBQb25nLCBhbmQgdGhlIGJvdHRvbSBvZiB0aGUgaW1hZ2UgZGVwaWN0cyB0aGUgcG90ZW50aWFsIGFjdGlvbiB2YWx1ZXMgdGhhdCBjYW4gYmUgZ2FpbmVkIGJ5IHRha2luZyB0aGVzZSBhY3Rpb25zLiBQb25nIGlzIGEgdHdvLXBsYXllciBnYW1lIHdoZXJlIHRoZSBnb2FsIGlzIHRvIGhpdCB0aGUgYmFsbCBwYXN0IHRoZSBvcHBvbmVudCdzIHBhZGRsZS4gVGhlIERRTiBhZ2VudCBjb250cm9scyBvbmUgb2YgdGhlIHBhZGRsZXMgKGdyZWVuIG9uZSkgYW5kIHRoZSBvcHBvbmVudCBpcyBjb250cm9sbGVkIGJ5IGEgc2ltcGxlIEFJLiBXZSBjYW4gdXNlIHRoZSBvcHRpbWFsIGFjdGlvbi12YWx1ZSBmdW5jdGlvbiB0byBkZXRlcm1pbmUgdGhlIG9wdGltYWwgcG9saWN5IHRoYXQgb3VyIGFnZW50IGNhbiBmb2xsb3cuIEF0IHRpbWUgcG9pbnQgMSwgdGhlIGJhbGwgaXMgbW92aW5nIHRvd2FyZHMgdGhlIHBhZGRsZSBjb250cm9sbGVkIGJ5IHRoZSBhZ2VudCBvbiB0aGUgcmlnaHQgc2lkZSBvZiB0aGUgc2NyZWVuIGFuZCB0aGUgcS12YWx1ZXMgb2YgYWxsIGFjdGlvbnMgYXJlIGFyb3VuZCAwLjcsIHJlZmxlY3RpbmcgdGhlIGV4cGVjdGVkIHZhbHVlIG9mIHRoaXMgc3RhdGUgYmFzZWQgb24gcHJldmlvdXMgZXhwZXJpZW5jZS4gQXQgdGltZSBwb2ludCAyLCB0aGUgYWdlbnQgc3RhcnRzIG1vdmluZyB0aGUgcGFkZGxlIHRvd2FyZHMgdGhlIGJhbGwgYW5kIHRoZSB2YWx1ZSBvZiB0aGUgJ3VwJyBhY3Rpb24gc3RheXMgaGlnaCB3aGlsZSB0aGUgdmFsdWUgb2YgdGhlICdkb3duJyBhY3Rpb24gZmFsbHMgdG8g4oiSMC45LiBUaGlzIHJlZmxlY3RzIHRoZSBmYWN0IHRoYXQgcHJlc3NpbmcgJ2Rvd24nIHdvdWxkIGxlYWQgdG8gdGhlIGFnZW50IGxvc2luZyB0aGUgYmFsbCBhbmQgaW5jdXJyaW5nIGEgcmV3YXJkIG9mIOKIkjEgKGxvc2luZyB0aGUgZ2FtZSkuIEF0IHRpbWUgcG9pbnQgMywgdGhlIGFnZW50IGhpdHMgdGhlIGJhbGwgYnkgcHJlc3NpbmcgJ3VwJywgYW5kIHRoZSBleHBlY3RlZCByZXdhcmQga2VlcHMgaW5jcmVhc2luZyB1bnRpbCB0aW1lIHBvaW50IDQsIHdoZW4gdGhlIGJhbGwgYm91bmNlcyB0byB0aGUgbGVmdCBzaWRlIG9mIHRoZSBzY3JlZW4gYW5kIHRoZSB2YWx1ZSBvZiBhbGwgYWN0aW9ucyByZWZsZWN0cyB0aGF0IHRoZSBhZ2VudCBpcyBhYm91dCB0byByZWNlaXZlIGEgcmV3YXJkIG9mIDEuIE5vdGUsIHRoZSBkYXNoZWQgbGluZSBzaG93cyB0aGUgcGFzdCB0cmFqZWN0b3J5IG9mIHRoZSBiYWxsIHB1cmVseSBmb3IgaWxsdXN0cmF0aXZlIHB1cnBvc2VzICh0aGF0IGlzLCBub3Qgc2hvd24gZHVyaW5nIHRoZSBnYW1lKS4KCmBgYHtyIHF2YWx1ZSwgZmlnLmNhcCA9ICJWaXN1YWxpemF0aW9uIG9mIHRoZSBsZWFybmVkIGFjdGlvbi12YWx1ZSBmdW5jdGlvbiBpbiB0aGUgZ2FtZSBQb25nIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImZpZ3VyZS1xdmFsdWUucG5nIikKYGBgCgojIyBRLW5ldHdvcms6IGFwcHJveGltYXRvciBvZiBRLXZhbHVlIGZ1bmN0aW9uCgpBY2hpZXZpbmcgb3B0aW1hbCB0YXJnZXQgdmFsdWVzOiAkciArIFxnYW1tYSBcbWF4X3thJ31RXioocycsYScpJCBpcyBoYXJkIGFuZCBjb21wdXRhdGlvbmFsbHkgZXhwZW5zaXZlLCBlc3BlY2lhbGx5IHdoZW4gdGhlIHN0YXRlIHNwYWNlIGlzIGxhcmdlLiBUbyBhZGRyZXNzIHRoaXMgaXNzdWUsIHdlIGNhbiBhcHByb3hpbWF0ZSB0aGUgdGFyZ2V0IHZhbHVlcyB1c2luZyBhIGRlZXAgbmV1cmFsIG5ldHdvcmsgYXMgYW4gYXBwcm94aW1hdG9yOiAKCiQkCnIgKyBcZ2FtbWEgXG1heF97YSd9UShzJyxhJztcdGhldGFfaV4tKQokJAoKd2hlcmUgJFx0aGV0YV9pXi0kIGFyZSB0aGUgcGFyYW1ldGVycyBmcm9tIHNvbWUgcHJldmlvdXMgaXRlcmF0aW9uLiBUaGUgUS1uZXR3b3JrIGFyZSBkZWZpbmVkIGFzIGEgbmV1cmFsIG5ldHdvcmsgYXBwcm94aW1hdG9yIHdpdGggd2VpZ2h0cyAkXHRoZXRhJC4gVGhlIFEtbmV0d29yayBpcyB0cmFpbmVkIHRvIG1pbmltaXplIHRoZSBtZWFuIHNxdWFyZSBlcnJvciBpbiB0aGUgbG9zcyBmdW5jdGlvbjogCgokJApMX2koXHRoZXRhX2kpID0gXG1hdGhiYntFfV97cyxhLHIscyd9WyhcdW5kZXJicmFjZXtyICsgXGdhbW1hIFxtYXhfe2EnfVEocycsYSc7XHRoZXRhX2leLSl9X3tcdGV4dHt0YXJnZXQgdmFsdWV9fSAtIFx1bmRlcmJyYWNle1EocyxhO1x0aGV0YV9pKX1fe1x0ZXh0e2VzdGltYXRpb259fSleMl0KJCQgCgphbmQgdGhlIGdyYWRpZW50IG9mIHRoZSBsb3NzIGZ1bmN0aW9uIGNhbiBiZSB3cml0dGVuIGFzOgoKJCQKXG5hYmxhX3tcdGhldGFfaX1MX2koXHRoZXRhX2kpID0gXG1hdGhiYntFfV97cyxhLHIscyd9XGxlZnRbXGxlZnQociArIFxnYW1tYSBcbWF4X3thJ31RKHMnLGEnO1x0aGV0YV9pXi0pIC0gUShzLGE7XHRoZXRhX2kpXHJpZ2h0KVxuYWJsYV97XHRoZXRhX2l9UShzLGE7XHRoZXRhX2kpXHJpZ2h0XQokJAoKVGhlIHRhcmdldCB2YWx1ZTogJHIgKyBcZ2FtbWEgXG1heF97YSd9UShzJyxhJztcdGhldGFfaV4tKSQgY2FuIGJlIHZpZXdlZCBhcyB0aGUgcmV3YXJkIHlvdSB3aWxsIGdldCB1c2luZyBwYXJhbWV0ZXJzIGZyb20gc29tZSBwcmV2aW91cyBpdGVyYXRpb24sIGFuZCB0aGUgZXN0aW1hdGlvbjogJFEocyxhO1x0aGV0YV9pKSQgaXMgdGhlIHJld2FyZCB5b3Ugd2lsbCBnZXQgdXNpbmcgdGhlIGN1cnJlbnQgcGFyYW1ldGVycy4gVGhlIGxvc3MgZnVuY3Rpb24gaXMgdGhlIHNxdWFyZWQgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0YXJnZXQgdmFsdWUgYW5kIHRoZSBlc3RpbWF0aW9uLgoKIyBEZWVwIFJlaW5mb3JjZW1lbnQgTGVhcm5pbmcKClRoZSBwYXBlciBieSBNbmloIGV0IGFsLiAoMjAxNSkgZXhwYW5kcyB0aGUgUS1sZWFybmluZyBhbGdvcml0aG0gd2hpY2ggZGVzY3JpYmVkIGFib3ZlIGJ5IHVzaW5nIGEgZGVlcCBuZXVyYWwgbmV0d29yayB0byBhcHByb3hpbWF0ZSB0aGUgUS12YWx1ZSBmdW5jdGlvbiB3aXRoIDMgbmV3IGZlYXR1cmVzIGNvdmVyZWQgYmVsb3cuCgojIyBDb252b2x1dGlvbmFsIG5ldHdvcmsgeyNuZXR3b3JrfQoKYGBge3IgbmV0d29yaywgZmlnLmNhcCA9ICJWaXN1YWxpemF0aW9uIG9mIHRoZSBuZXR3b3JrIGFyY2hpdGVjdHVyZSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJmaWctbmV0d29yay5wbmciKQpgYGAKClRoZSBmaXJzdCBuZXcgZmVhdHVyZXMgdXNlZCBpbiB0aGUgcGFwZXIgaXMgdGhlIGNvbnZvbHV0aW9uYWwgbmV0d29yayB1c2VkIHRvIHByb2Nlc3MgdGhlIGltYWdlIG9mIHRoZSBnYW1lcyBpbnRvIHN0YXRlcyB3aGljaCB3aWxsIGJlIGZlZCBpbnRvIHRoZSBRLW5ldHdvcmsgKHNob3duIGluIEZpZyBcQHJlZihmaWc6bmV0d29yaykpLgoKVGhlIGRldGFpbHMgb2Ygd2hhdCBpcyBhIGNvbnZvbHV0aW9uYWwgbmV0d29yayBpcyBiZXlvbmQgdGhlIHNjb3BlIG9mIHRoaXMgcHJvamVjdCwgYnV0IGluIHNob3J0LCBhIGNvbnZvbHV0aW9uYWwgbmV0d29yayBpcyBhIHR5cGUgb2YgbmV1cmFsIG5ldHdvcmsgdGhhdCBpcyB3ZWxsLXN1aXRlZCBmb3IgcHJvY2Vzc2luZyBpbWFnZXMuIE1vcmUgY2FuIGJlIGZvdW5kIGluIHRoZSBmb2xsb3dpbmcgdmlkZW8gbWFkZSBieSAzQmx1ZTFCcm93bi4KYGBge3IgY29uLXZpZGVvLCBmaWcuY2FwPSAiQnV0IHdoYXQgaXMgYSBjb252b2x1dGlvbj8ifQojIEVtYmVkZGluZyB2aWRlbyB1c2luZyBwYWNrYWdlIGZyb20gaHR0cHM6Ly9pamx5dHRsZS5naXRodWIuaW8vdmVtYmVkci8KbGlicmFyeSgidmVtYmVkciIpCmVtYmVkX3VybCgiaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1LdVhqd0I0THpTQSIpICAlPiUKICB1c2VfYWxpZ24oImNlbnRlciIpCmBgYAoKCkJlZm9yZSBmZWVkaW5nIHRoZSBpbWFnZXMgdG8gdGhlIG5ldHdvcmssIHNvbWUgcHJlLXByb2Nlc3NpbmcgbmVlZCB0byBiZSBkb25lIGFzIHRoZSByYXcgaW1hZ2VzIG9mIHRoZSBlbXVsYXRvciAoQXRhcmkgMjYwMCksIHdoaWNoIGFyZSAyMTAgw5cgMTYwIHBpeGVsIGltYWdlcyB3aXRoIGEgMTI4LWNvbG91ciBwYWxldHRlLCBpcyBkZW1hbmRpbmcgaW4gdGVybXMgb2YgY29tcHV0YXRpb24gYW5kIG1lbW9yeSByZXF1aXJlbWVudHMuIEZpcnN0LCB0byBlbmNvZGUgYSBzaW5nbGUgZnJhbWUgdGhlIGF1dGhvcnMgdGFrZSB0aGUgbWF4aW11bSB2YWx1ZSBmb3IgZWFjaCBwaXhlbCBjb2xvciB2YWx1ZSBvdmVyIHRoZSBmcmFtZSBiZWluZyBlbmNvZGVkIGFuZCB0aGUgcHJldmlvdXMgZnJhbWUuIFRoaXMgaXMgbmVjZXNzYXJ5IHRvIHJlbW92ZSBmbGlja2VyaW5nIHRoYXQgaXMgcHJlc2VudCBpbiBnYW1lcyB3aGVyZSBzb21lIG9iamVjdHMgYXBwZWFyIG9ubHkgaW4gZXZlbiBmcmFtZXMgd2hpbGUgb3RoZXIgb2JqZWN0cyBhcHBlYXIgb25seSBpbiBvZGQgZnJhbWVzLCBhbiBhcnRlZmFjdCBjYXVzZWQgYnkgdGhlIGxpbWl0ZWQgbnVtYmVyIG9mIHNwcml0ZXMgQXRhcmkgMjYwMCBjYW4gZGlzcGxheSBhdCBvbmNlLiBTZWNvbmQsIGF1dGhvcnMgdGhlbiBleHRyYWN0IHRoZSBZIGNoYW5uZWwsIGFsc28ga25vd24gYXMgbHVtaW5hbmNlLCBmcm9tIHRoZSBSR0IgZnJhbWUgYW5kIHJlc2NhbGUgaXQgdG8gODQgw5cgODQuIFRoZSBmdW5jdGlvbiAoJFxwaGkkKSBmcm9tIGFsZ29yaXRobSAxIGRlc2NyaWJlZCBiZWxvdyBhcHBsaWVzIHRoaXMgcHJlLXByb2Nlc3NpbmcgdG8gdGhlIDQgbW9zdCByZWNlbnQgZnJhbWVzIGFuZCBzdGFja3MgdGhlbSB0byBwcm9kdWNlIHRoZSBpbnB1dCB0byB0aGUgUS1mdW5jdGlvbi4KCkFmdGVyIGltYWdlIHByZXByb2Nlc3NpbmcsIGFuIDg0IMOXIDg0IMOXIDQgaW1hZ2Ugd2lsbCBiZSB1c2VkIHRvIHRyYWluIHRoZSBuZXR3b3JrLiBUaGUgZmlyc3QgaGlkZGVuIGxheWVyIGNvbnZvbHZlcyAzMiBmaWx0ZXJzIG9mIDggw5cgOCB3aXRoIHN0cmlkZSA0IHdpdGggdGhlIGlucHV0IGltYWdlIGFuZCBhcHBsaWVzIGEgcmVjdGlmaWVyIG5vbmxpbmVhcml0eS4gVGhlIHNlY29uZCBoaWRkZW4gbGF5ZXIgY29udm9sdmVzIDY0IGZpbHRlcnMgb2YgNCDDlyA0IHdpdGggc3RyaWRlIDIsIGFnYWluIGZvbGxvd2VkIGJ5IGEgcmVjdGlmaWVyIG5vbmxpbmVhcml0eS4gVGhpcyBpcyBmb2xsb3dlZCBieSBhIHRoaXJkIGNvbnZvbHV0aW9uYWwgbGF5ZXIgdGhhdCBjb252b2x2ZXMgNjQgZmlsdGVycyBvZiAzIMOXIDMgd2l0aCBzdHJpZGUgMSBmb2xsb3dlZCBieSBhIHJlY3RpZmllci4gVGhlIGZpbmFsIGhpZGRlbiBsYXllciBpcyBmdWxseS1jb25uZWN0ZWQgYW5kIGNvbnNpc3RzIG9mIDUxMiByZWN0aWZpZXIgdW5pdHMuIFRoZSBvdXRwdXQgbGF5ZXIgaXMgYSBmdWxseS1jb25uZWN0ZWQgbGluZWFyIGxheWVyIHdpdGggYSBzaW5nbGUgb3V0cHV0IGZvciBlYWNoIHZhbGlkIGFjdGlvbiAoUS12YWx1ZSkuCgpgYGB7ciB0c25lLCBmaWcuY2FwID0gIlR3by1kaW1lbnNpb25hbCB0LVNORSB2aXN1YWxpemF0aW9uIG9mIHRoZSByZXByZXNlbnRhdGlvbnMgaW4gdGhlIGxhc3QgaGlkZGVuIGxheWVyIGFzc2lnbmVkIGJ5IHRoZSBuZXR3b3JrIHRvIGdhbWUgc3RhdGVzIGV4cGVyaWVuY2VkIHdoaWxlIHBsYXlpbmcgU3BhY2UgSW52YWRlcnMifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiZmlndXJlLXRzbmUucG5nIikKYGBgCgpGaWcgXEByZWYoZmlnOnRzbmUpIHNob3dzIHRoYXQgdGhlIGNvbnZvbHV0aW9uYWwgbmV0d29yayBpcyBhYmxlIHRvIGxlYXJuIHRoZSByZXByZXNlbnRhdGlvbiBvZiB0aGUgZ2FtZSBzdGF0ZXMgdGhhdCBhcmUgdXNlZnVsIGZvciBwcmVkaWN0aW5nIHRoZSBRLXZhbHVlcyBvZiB0aGUgYWN0aW9ucy4gVGhlIHBsb3RzIGFyZSBnZW5lcmF0ZWQgYnkgcnVubmluZyB0LVNORSBhbGdvcml0aGVtIG9uIHRoZSBsYXN0IGhpZGRlbiBsYXllciByZXByZXNlbnRhdGlvbnMgYXNzaWduZWQgYnkgdGhlIG5ldHdvcmsgdG8gZ2FtZSBzdGF0ZXMgZXhwZXJpZW5jZWQgd2hpbGUgcGxheWluZyBTcGFjZSBJbnZhZGVycy4gVGhlIGRvdHMgYXJlIGNvbG9yZWQgYWNjb3JkaW5nIHRvICRWJDogdGhlIG1heGltdW0gZXhwZWN0ZWQgcmV3YXJkIG9mIGEgc3RhdGUgcHJlZGljdGVkIGJ5IHRoZSBhZ2VudCAobmV0d29yaykgZm9yIHRoZSBjb3JyZXNwb25kaW5nIGdhbWUgc3RhdGVzLiBUaGUgYWdlbnQgY2FuIHNlcGFyYXRlIGZ1bGwgc2NyZWVucyBhbmQgbmVhcmx5IGVtcHR5IHNjcmVlbnMgd2hpbGUgcHJlZGljdGluZyBib3RoIG9mIHRoZW0gd2l0aCBoaWdoZXN0IHZhbHVlcyBiZWNhdXNlIGl0IGxlYXJuZWQgdGhhdCBjb21wbGV0aW5nIGEgc2NyZWVuIGxlYWRzIHRvIGEgbmV3IHNjcmVlbiBmdWxsIG9mIGVuZW15IHNoaXBzLiBQYXJ0aWFsbHkgY29tcGxldGVkIHNjcmVlbnMgYXJlIGFsc28gc2VwYXJhdGVkIGZyb20gdGhlIGZ1bGwgYW5kIGVtcHR5IHNjcmVlbnMsIGFuZCB0aGUgYWdlbnQgcHJlZGljdHMgdGhlbSB3aXRoIGxvd2VyIHZhbHVlcyBiZWNhdXNlIGxlc3MgcmV3YXJkIGlzIGF2YWlsYWJsZS4gVGhlIGFnZW50IGFsc28gbGVhcm5lZCB0byBuZWdsZWN0IHRoZSB0aGUgb3JhbmdlIGJ1bmtlcnMgd2hlbiB0aGUgZ2FtZSBpcyBuZWFyIHRvIHRoZSBuZXh0IGxldmVsIGFzIHRoZXkgYXJlIG5vdCBpbXBvcnRhbnQgZm9yIHRoZSByZXdhcmQuCgojIyBFeHBlcmllbmNlIHJlcGxheQoKVGhlIHNlY29uZCBuZXcgZmVhdHVyZSBpcyB0aGUgZXhwZXJpZW5jZSByZXBsYXkuIFRoZSBleHBlcmllbmNlIHJlcGxheSBpcyBhIHRlY2huaXF1ZSB0aGF0IHJhbmRvbWx5IHNhbXBsZXMgcHJldmlvdXMgZXhwZXJpZW5jZXMgZnJvbSB0aGUgYWdlbnQncyBtZW1vcnkgYW5kIHVzZXMgdGhlbSB0byB0cmFpbiB0aGUgbmV0d29yay4gVGhpcyB0ZWNobmlxdWUgaXMgdXNlZCB0byBicmVhayB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBjb25zZWN1dGl2ZSBzYW1wbGVzIGFuZCBzdGFiaWxpemUgdGhlIGxlYXJuaW5nIHByb2Nlc3MuIEluIG9yZGVyIHdvcmRzLCBpdCBwcmV2ZW50IHRoZSBhZ2VudCBmcm9tIGxlYXJuaW5nIGZyb20gb25seSByZWNlbnQgZXhwZXJpZW5jZXMsIHdoaWNoIGFyZSBoaWdobHkgY29ycmVsYXRlZCB3aXRoIGN1cnJlbnQgc3RhdGUuIFRoZSBleHBlcmllbmNlIHJlcGxheSBzdG9yZXMgdGhlIGFnZW50J3MgZXhwZXJpZW5jZXMgaW4gYSByZXBsYXkgbWVtb3J5LCB3aGljaCBpcyBhIGRhdGFzZXQgb2YgdHVwbGVzICQocywgYSwgciwgcycpJC4gT25seSB0aGUgY3VycmVudCBzdGF0ZSAkcyQsIGFjdGlvbiAkYSQsIHJld2FyZCAkciQsIGFuZCBuZXh0IHN0YXRlICRzJyQgYXJlIHN0b3JlZCBhcyBhbnkgcHJldmlvdXMgaW5mb3JtYXRpb24gaXMgaXJyZWxldmFudCBmb3IgdGhlIGFnZW50IHRvIG1ha2UgdGhlIGFjdGlvbi4gVGhlIGFnZW50IHNhbXBsZXMgYSBtaW5pYmF0Y2ggb2YgZXhwZXJpZW5jZXMgZnJvbSB0aGUgcmVwbGF5IG1lbW9yeSBhbmQgdXNlcyB0aGVtIHRvIHRyYWluIHRoZSBuZXR3b3JrLiBUaGUgcmVwbGF5IG1lbW9yeSBoYXMgYSBmaXhlZCBzaXplIGFuZCB3aGVuIGl0IGlzIGZ1bGwsIHRoZSBvbGRlc3QgZXhwZXJpZW5jZXMgYXJlIHJlbW92ZWQgdG8gbWFrZSBzcGFjZSBmb3IgbmV3IGV4cGVyaWVuY2VzLgoKIyMgU2Vjb25kIHRhcmdldCBuZXR3b3JrICRcd2lkZWhhdHtRfSQKClRoZSB0aGlyZCBuZXcgZmVhdHVyZSBpcyB0aGUgaW50cm9kdWN0aW9uIG9mIHRoZSB0YXJnZXQgbmV0d29yayAkXGhhdHtRfSQuIFRoZSBhZ2VudCB3aWxsIHVzZSAkXGhhdHtRfSQgaW5zdGVhZCBvZiAkUSQgdG8gZ2VuZXJhdGVzIHRhcmdldHMgJHlfaiA9IHIgKyBcZ2FtbWEgXG1heF97YSd9UShzJyxhJztcdGhldGFfaV4tKSQgb24gZWFjaCB1cGRhdGUuIFRoaXMgd2lsbCBtYWtlIGRpdmVyZ2VuY2UgbGVzcyBsaWtlbHkgdG8gaGFwcGVuLCBhcyBhbiB1cGRhdGUgdGhhdCBpbmNyZWFzZSAkUShzX3QsIGFfdCkkIG9mdGVuIGFsc28gaW5jcmVhc2UgJFEoc197dCsxfSxhKSBcZm9yYWxsIGEkLiBUaGlzIGltcGxpZXMgdGhhdCB0aGUgdGFyZ2V0ICR5X2okIG1pZ2h0IGJlIGluY3JlYXNpbmcgYXMgd2VsbCwgbGVhZGluZyB0byBvc2NpbGxhdGlvbnMgb3IgZGl2ZXJnZW5jZS4gQnkgc2V0aW5nIHRoZSB0YXJnZXQgYmVpbmcgY2FsY3VsYXRlZCBieSBhbm90aGVyIG5ldHdvcmsgd2hpY2ggbm8gbm90IHVwZGF0ZSBmb3IgYSBjZXJ0YWluIHBlcmlvZCwgdGhlIHRhcmdldCB3aWxsIGJlIGZpeGVkIGZvciBhIHdoaWxlLCBhbmQgbWFraW5nIHRoZSBsZWFybmluZyBwcm9jZXNzIG1vcmUgc3RhYmxlLiBUaGUgdGFyZ2V0IG5ldHdvcmsgaW4gdGhpcyBzdHVkeSBpcyBzZXQgdG8gYmUgYSBjb3B5IG9mIHRoZSBRLW5ldHdvcmssIGJ1dCBvbmx5IHVwZGF0ZWQgcGVyaW9kaWNhbGx5LgoKfCBHYW1lICAgICAgICAgICB8IFdpdGggcmVwbGF5LCB3aXRoIHRhcmdldCBRIHwgV2l0aCByZXBsYXksIHdpdGhvdXQgdGFyZ2V0IFEgfCBXaXRob3V0IHJlcGxheSwgd2l0aCB0YXJnZXQgUSB8IFdpdGhvdXQgcmVwbGF5LCB3aXRob3V0IHRhcmdldCBRIHwKfC0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLXwtLS0tLS0tLS0tLS0tLS18CnwgQnJlYWtvdXQgICAgICAgfCAzMTYuOCAgICAgICAgICAgICAgICAgICAgICB8IDI0MC43ICAgICAgICAgICAgICAgICAgICAgICAgIHwgMTAuMiAgICAgICAgICAgICAgICAgICAgICAgICAgfCAzLjIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgRW5kdXJvICAgICAgICAgfCAxMDA2LjMgICAgICAgICAgICAgICAgICAgICB8IDgzMS40ICAgICAgICAgICAgICAgICAgICAgICAgIHwgMTQxLjkgICAgICAgICAgICAgICAgICAgICAgICAgfCAyOS4xICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgUml2ZXIgUmFpZCAgICAgfCA3NDQ2LjYgICAgICAgICAgICAgICAgICAgICB8IDQxMDIuOCAgICAgICAgICAgICAgICAgICAgICAgIHwgMjg2Ny43ICAgICAgICAgICAgICAgICAgICAgICAgfCAxNDUzLjAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgU2VhcXVlc3QgICAgICAgfCAyODk0LjQgICAgICAgICAgICAgICAgICAgICB8IDgyMi42ICAgICAgICAgICAgICAgICAgICAgICAgIHwgMTAwMy4wICAgICAgICAgICAgICAgICAgICAgICAgfCAyNzUuOCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgU3BhY2UgSW52YWRlcnMgfCAxMDg4LjkgICAgICAgICAgICAgICAgICAgICB8IDgyNi4zICAgICAgICAgICAgICAgICAgICAgICAgIHwgMzczLjIgICAgICAgICAgICAgICAgICAgICAgICAgfCAzMDIuMCAgICAgICAgICAgICAgICAgICAgICAgICAgICB8Cgo6ICgjdGFiOnRhYjEpIENvbXBhcmlzb24gb2YgdGhlIHBlcmZvcm1hbmNlIG9mIHRoZSBkZWVwIFEtbmV0d29yayBhZ2VudCB3aXRoIGFuZCB3aXRob3V0IGV4cGVyaWVuY2UgcmVwbGF5IGFuZCB0YXJnZXQgUS1uZXR3b3JrIG9uIGZpdmUgQXRhcmkgMjYwMCBnYW1lcy4gVGhlIHZhbHVlIHJlcHJlc2VudGluZyB0aGUgaGlnaGVzdCBhdmVyYWdlIGVwaXNvZGUgc2NvcmUuIFRoZSByZXN1bHRzIGFyZSB0YWtlbiBmcm9tIE1uaWggZXQgYWwuICgyMDE1KQoKVGFibGUgXEByZWYodGFiOnRhYjEpIHNob3dzIHRoYXQgdGhlIGFnZW50IGJlbmVmaXRzIGZyb20gYm90aCBleHBlcmllbmNlIHJlcGxheSBhbmQgdGFyZ2V0IFEtbmV0d29yaywgd2l0aCB0aGUgaGlnaGVzdCBzY29yZSBhY2hpZXZlZCB3aGVuIGJvdGggdGVjaG5pcXVlcyBhcmUgdXNlZC4KCiMjIE1vZGVsIFRyYWluaW5nCgpUaGUgYXV0aG9ycyB1c2UgUk1TUHJvcCBhbGdvcml0aG0gKFJvb3QgTWVhbiBTcXVhcmUgUHJvcGFnYXRpb24sIGlzIGFuIG9wdGltaXphdGlvbiBhbGdvcml0aG0gaXMgYW4gZXh0ZW5zaW9uIG9mIFN0b2NoYXN0aWMgR3JhZGllbnQgRGVzY2VudCAoU0dEKSBhbGdvcml0aG0gaW4gdHJhbm5pbmcgZGVlcCBuZXVyYWwgbmV0d29ya3MpIHdpdGggYSBtaW5pLWJhdGNoIHNpemUgb2YgMzIgdG8gdHJhaW4gdGhlIGFnZW50IFtAcm1zcHJvcF0uIFRoZSBiZWhhdmlvciBwb2xpY3kgZHVyaW5nIHRyYWluaW5nIGlzICRcZXBzaWxvbiQtZ3JlZWR5IHBvbGljeSB3aXRoICRcZXBzaWxvbiQgYW5uZWFsZWQgbGluZWFybHkgZnJvbSAxIHRvIDAuMSBvdmVyIHRoZSBmaXJzdCBtaWxsaW9uIGZyYW1lcyBhbmQgZml4ZWQgYXQgMC4xIHRoZXJlYWZ0ZXIuIFRoZSBhZ2VudCBpcyB0cmFpbmVkIGZvciA1MCBtaWxsaW9uIGZyYW1lcywgd2hpY2ggaXMgZXF1aXZhbGVudCB0byAzOCBkYXlzIG9mIGdhbWUgdGltZSBhbmQgdXNlIGEgcmVwbGF5IG1lbW9yeSBvZiBzaXplIG9uZSBtaWxsaW9uLiBCb3RoIHRoZSByZXdhcmQgYW5kIGVycm9ycyAoJHIgKyBcZ2FtbWEgXG1heF97YSd9UShzJyxhJztcdGhldGFfaV4tKSAtIFEocyxhO1x0aGV0YV9pKSQpIGFyZSBjbGlwcGVkIGF0IFstMSwxXSwgYW5kIGFjdGlvbnMgYXJlIG9ubHkgc2VsZWN0ZWQgb24gZXZlcnkgZm91cnRoIGZyYW1lIGFuZCByZXBlYXRlZCBmb3IgdGhlIG5leHQgdGhyZWUgZnJhbWVzLiBUaGlzIGlzIGxlc3MgZXhwZW5zaXZlIHRvIHJ1biBhbmQgaGFzIGxpdHRsZSBlZmZlY3Qgb24gdGhlIHBlcmZvcm1hbmNlIGFzIHRoZSBmYXN0ZXN0IGh1bWFuZSBwbGF5ZXIgY2FuIG9ubHkgcmVhY3QgZXZlcnkgNnRoIGZyYW1lLiBIeXBlcnBhcmFtZXRlcnMgYXJlIHNlbGVjdGVkIGJ5ICdpbmZvcm1hbCBzZWFyY2gnIChub3QgZS5nLiBncmlkIHNlYXJjaCkuCgojIyBBbGdvcml0aG0KCioqRGVlcCBRLWxlYXJuaW5nIHdpdGggZXhwZXJpZW5jZSByZXBsYXkqKi4gICAgCmBgYHtyIGFsZ29yaXRobSwgZmlnLmNhcCA9ICIifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiZmlndXJlLWFsZ29yaXRobS5wbmciKQpgYGAKClRoZSBhYm92ZSBhbGdvcml0aG0gaXMgZGVzY3JpYmUgaW4gdGhlIHBhcGVyIGJ5IE1uaWggZXQgYWwuICgyMDE1KSBhbmQgaXMgdXNlZCB0byB0cmFpbiB0aGUgZGVlcCBRLW5ldHdvcmsgYWdlbnQuIEVwaXNvZGUgaXMgZGVmaW5lZCBhcyBhbiBlbnRpcmUgZ2FtZXBsYXkuIFRoZSBncmVlZHkgcGFyYW1ldGVyICRcZXBzaWxvbiQgYWxsb3dzIHRoZSBhZ2VudCB0byBleHBsb3JlIHRoZSBlbnZpcm9ubWVudCBieSBzZWxlY3RpbmcgYSByYW5kb20gYWN0aW9uIHdpdGggcHJvYmFiaWxpdHkgJFxlcHNpbG9uJCBhbmQgc2VsZWN0aW5nIHRoZSBhY3Rpb24gd2l0aCB0aGUgaGlnaGVzdCBRLXZhbHVlIHdpdGggcHJvYmFiaWxpdHkgJDEtXGVwc2lsb24kLiAkXHBoaSQgaXMgdGhlIHByZXByb2Nlc3NpbmcgZnVuY3Rpb24gZGVzY3JpYmVkIGFib3ZlIGluIFxAcmVmKG5ldHdvcmspLiBUaGUgdGFyZ2V0IHZhbHVlICR5X2okIGlzIGNhbGN1bGF0ZWQgYXMgJHJfaiQgaWYgdGhlIG5leHQgc3RhdGUgaXMgdGVybWluYWwoZW5kIG9mIHRoZSBnYW1lKSwgYW5kICRyX2ogKyBcZ2FtbWEgXG1heF97YSd9IFxoYXR7UX0oXHBoaV97aisxfSwgYSc7IFx0aGV0YV4tKSQgaWYgdGhlIG5leHQgc3RhdGUgaXMgbm9uLXRlcm1pbmFsLiBUaGUgdGFyZ2V0IG5ldHdvcmsgJFxoYXR7UX0kIGlzIHVwZGF0ZWQgZXZlcnkgJEMgPSAxMDAwMCQgc3RlcHMgdG8gYmUgdGhlIHNhbWUgYXMgdGhlIFEtbmV0d29yayAkUSQuIAoKIyMgUmVzdWx0cyBhbmQgZXZhbHVhdGlvbgoKYGBge3IgdHJhaW5jdXJ2ZSwgZmlnLmNhcCA9ICJUcmFuaW5nIGN1cnZlcyB0cmFja2luZyB0aGUgYWdlbnQncyBhdmVyYWdlIHNjb3JlIGFuZCBhdmVyYWdlIHByZWRpY3RlZCBhY3Rpb24gdmFsdWUifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiZmlndXJlLXRyYWluY3VydmUucG5nIikKYGBgCgpGaWcgXEByZWYoZmlnOnRyYWluY3VydmUpIHNob3dzIHRoZSBhZ2VudCdzIGF2ZXJhZ2Ugc2NvcmUgYW5kIGF2ZXJhZ2UgcHJlZGljdGVkIGFjdGlvbiB2YWx1ZSBvdmVyIHRoZSBjb3Vyc2Ugb2YgdHJhaW5pbmcuIFRoZSBhZ2VudCdzIGF2ZXJhZ2Ugc2NvcmUgaW5jcmVhc2VzIG92ZXIgdGltZSBhcyB0aGUgYWdlbnQgbGVhcm5zIHRvIHBsYXkgdGhlIGdhbWUgYmV0dGVyLiBUaGUgYXZlcmFnZSBwcmVkaWN0ZWQgYWN0aW9uIHZhbHVlIGFsc28gaW5jcmVhc2VzIG92ZXIgdGltZSwgaW5kaWNhdGluZyB0aGF0IHRoZSBhZ2VudCBpcyBsZWFybmluZyB0byBwcmVkaWN0IHRoZSB2YWx1ZSBvZiBhY3Rpb25zIG1vcmUgYWNjdXJhdGVseS4gSW4gZWFzaWVyIGdhbWVzLCB0aGUgYWdlbnQgbGVhcm5zIHRvIHBsYXkgdGhlIGdhbWUgd2VsbCBxdWlja2x5LCBhbmQgdGhlIHNjb3JlcyBhcmUgbW9yZSBzdGFibGUsIHdoaWxlIGluIGhhcmRlciBnYW1lcywgdGhlIGFnZW50IHRha2VzIGxvbmdlciB0byBsZWFybiB0byBwbGF5IHRoZSBnYW1lIHdlbGwgYW5kIHRoZSByZXN1bHRzIGFyZSBtb3JlIHZhcmlhYmxlLgoKYGBge3IgZXZhbHVhdGlvbiwgZmlnLmNhcCA9ICJDb21wYXJpc29uIG9mIHRoZSBEUU4gYWdlbnQgd2l0aCB0aGUgYmVzdCByZWluZm9yY2VtZW50IGxlYXJuaW5nIG1ldGhvZHMgaW4gdGhlIGxpdGVyYXR1cmUifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiZmlndXJlLWV2YWx1YXRpb24ucG5nIikKYGBgCgpUaGUgYXV0aG9ycyBmdXJ0aGVyIGV2YWx1YXRlIHRoZSBhZ2VudCBpbiBhbGwgNDkgZ2FtZXMgaW4gdGhlIEF0YXJpIDI2MDAgcGxhdGZvcm0gYW5kIGNvbXBhcmUgdGhlIHJlc3VsdHMgd2l0aCB0aGUgYmVzdCByZWluZm9yY2VtZW50IGxlYXJuaW5nIG1ldGhvZHMgaW4gdGhlIGxpdGVyYXR1cmUuIEZpZyBcQHJlZihmaWc6ZXZhbHVhdGlvbikgc3VtbWFyaXplcyB0aGUgcmVzdWx0cyBvZiB0aGUgY29tcGFyaXNvbi4gVGhlIGRlZXAgUS1uZXR3b3JrIGFnZW50IG91dHBlcmZvcm1zIHRoZSBiZXN0IHJlaW5mb3JjZW1lbnQgbGVhcm5pbmcgbWV0aG9kcyBhbmQgcHJvZmVzc2lvbmFsIGh1bWFuIGdhbWUgdGVzdGVycyBpbiBtYWpvcml0eSBvZiB0aGUgZ2FtZXMuIEl0IGlzIHdvcnRoIHRvIG5vdGljZSB0aGF0IHRoZSBiZXN0IHBlcmZvcm1hbmNlIGdhbWVzIGFyZSB0aG9zZSB0aGF0IGFyZSBlYXNpZXIgdG8gcGxheSBhbmQgZWFzaWVyIGZvciB0aGUgYWdlbnQgdG8gbGVhcm4gKGUuZzogQm94aW5nLCBCcmVha291dCwgU3RhciBndW5uZXIsIFBpbmJhbGwsIGV0Yy4pLiBUaGVzZSBnYW1lcyBhcmUgc2hvcnQtaG9yaXpvbiBnYW1lcywgYW5kIHRoZSB1bHRpbWF0ZSBnYW1lIHN0cmF0ZWd5IGlzIHRvIGdhaW4gbW9yZSBwb2ludHMgaW4gdGhlIGdhbWUuIEhvd2V2ZXIsIGZvciBsb25nLWhvcml6b24gZ2FtZXMgdGhhdCBkb2Vzbid0IGdpdmUgaW1tZWRpYXRlIHJld2FyZCBhbmQgd2l0aCBhIGxvbmdlciB0aW1lIHRvIGFjaGlldmUgYSBnb2FsLCB0aGUgYWdlbnQgbWF5IG5vdCBwZXJmb3JtIHdlbGwgaW4gdGhlc2UgY2FzZXMuIE1vbnRlenVtYSdzIFJldmVuZ2UgaXMgb25lIG9mIHRoZSBoYXJkZXN0IGdhbWVzIGluIHRoZSBBdGFyaSAyNjAwIHBsYXRmb3JtLCB3aGljaCByZXF1aXJlIHRoZSBwbGF5ZXIgdG8gY29udHJvbCB0aGUgY2hhcmFjdGVyIHRvIGV4cGxvcmUgdGhlIG1hemUgYW5kIGNvbGxlY3QgdGhlIGtleXMgdG8gb3BlbiB0aGUgZG9vcnMgKHNob3dlZCBpbiBGaWcgXEByZWYoZmlnOm1vbnQpIFtAaW1hZ2VdKS4gVGhlIHJld2FyZCB3aWxsIG9ubHkgYmUgZ2l2ZW4gd2hlbiB0aGUgcGxheWVyIHJlYWNoIHRoZSBlbmQgb2YgdGhlIG1hemUgYW5kIG9wZW4gdGhlIGRvb3IuIFRoZSBldmFsdWF0aW9uIHNob3dzIHRoYXQgdGhlIGFnZW50IGlzIGJhc2ljYWxseSBwbGF5IHRoZSBnYW1lIHJhbmRvbWx5ICgwJSBwZXJmb3JtYW5jZSkgYW5kIGRpZCBub3QgbGVhcm4gdGhlIHN0cmF0ZWd5IHRvIHBsYXkgdGhlIGdhbWUuCgpgYGB7ciBtb250LCBmaWcuY2FwID0gIlNjcmVlbnNob3Qgb2YgdGhlIGdhbWUgTW9udGV6dW1hJ3MgUmV2ZW5nZSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJmaWd1cmUtbW9udC5wbmciKQpgYGAKClRoaXMgdmlkZW8gW0B5b3V0dWJlXSBkZW1vbnN0cmF0ZXMgaG93IGFuIGFnZW50IGlzIHNob3dpbmcgaW1wcm92ZW1lbnQgb3ZlciB0cmFpbmluZyBlcGlzb2RlcyBhbmQgaXMgYWJsZSB0byBwaWNrIHVwIHRoZSBvcHRpbWFsIHN0cmF0ZWd5IGF0IGhpdHRpbmcgdGhlIGJyaWNrcyB0byBnYWluIGhpZ2hlciBzY29yZSBpbiB0aGUgZ2FtZS4gCgpgYGB7ciB2aWRlbywgZmlnLmNhcD0gIkRRTiB0cmFpbmluZyBmb3IgcGxheWluZyBBdGFyaSdzIEJyZWFrb3V0IGdhbWUifQojIEVtYmVkZGluZyB2aWRlbyB1c2luZyBwYWNrYWdlIGZyb20gaHR0cHM6Ly9pamx5dHRsZS5naXRodWIuaW8vdmVtYmVkci8KbGlicmFyeSgidmVtYmVkciIpCmVtYmVkX3VybCgiaHR0cHM6Ly95b3V0dS5iZS9UbVBmVHBqdGRnZyIpICAlPiUKICB1c2VfYWxpZ24oImNlbnRlciIpCmBgYAoKIyMgU3VtbWFyeQotICAgRGVlcCByZWluZm9yY2VtZW50IGxlYXJuaW5nIGlzIGFkYXB0ZWQgZnJvbSByZWluZm9yY2VtZW50IGxlYXJuaW5nLCB3aXRoIGFiaWxpdGllcyB0byBsZWFybiBtb3JlIGNvbXBsZXggcG9saWNpZXMgZnJvbSBoaWdoIGRpbWVuc2lvbmFsIHNlbnNvcnkgaW5wdXQKLSAgIFRoZSBnb2FsIGlzIHRvIG1heGltaXplIHRoZSBjdW11bGF0aXZlIGZ1dHVyZSByZXdhcmQKLSAgIERRTiBpbXBsZW1lbnRzIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHdvcmsgYXMgYW4gYXBwcm94aW1hdG9yIGZvciB0aGUgdGFyZ2V0IHZhbHVlcyAKLSAgIFBlcmZvcm0gZXhwZXJpZW5jZSByZXBsYXkgdG8gcmVtb3ZlIGNvcnJlbGF0aW9ucyBhbmQgc3RvcmVzIHRoZSBhZ2VudCdzIGV4cGVyaWVuY2VzIGluIGEgcmVwbGF5IG1lbW9yeQotICAgTWluaW1pemUgdGhlIG1lYW4gc3F1YXJlIGVycm9yIGJldHdlZW4gUS1uZXR3b3JrIGFuZCBRLWxlYXJuaW5nIHRhcmdldAotICAgVGhlIGFsZ29yaXRobSB1c2VzIFJNU1Byb3Agd2l0aCBzdG9jaGFzdGljIGdyYWRpZW50IGRlc2NlbnQgdG8gdXBkYXRlIHRoZSB3ZWlnaHRzCi0gICBUaGUgRFFOIHdhcyB0ZXN0ZWQgb24gNDkgQXRhcmkgMjYwMCBnYW1lcywgYW5kIGl0IG91dHBlcmZvcm1zIG90aGVyIHJlaW5mb3JjZW1lbnQgbGVhcm5pbmcgYWxnb3JpdGhtcwoKCiMgUmVmZXJlbmNlcwoKOjo6IHsjcmVmc30KOjo6Cg==